﻿Imports System.Drawing




<Serializable()> Public Class GEOHelper

    Dim workingFilename As String = ""
    Dim imgheight As Double

    Dim produceFileName As String
    Dim geoFileName As String


    Public Sub New()

    End Sub
    Public Function GetWorldFile(ByVal fn As String, ByVal pt1x As Double, ByVal pt1y As Double, ByVal pt2x As Double, ByVal pt2y As Double, ByVal pt1lat As Double, ByVal pt1long As Double, ByVal pt2lat As Double, ByVal pt2long As Double) As String()
        workingFilename = fn

        Dim img As New MapWinGIS.Image
        img.Open(fn, MapWinGIS.ImageType.USE_FILE_EXTENSION, False)
        imgheight = img.Height

        img.Close()


        '上面的赋值完毕以后，开始以两点位基础，计算第三点
        Dim pt3x As Double = pt2x
        Dim pt3y As Double = pt1y

        Dim pt3lat As Double = pt1lat
        Dim pt3long As Double = pt2long


        Me.DoCompute(pt1x, pt2x, pt3x, pt1y, pt2y, pt3y, pt1lat, pt2lat, pt3lat, pt1long, pt2long, pt3long)


        '关于返回值
        Dim strList(1) As String
        strList(0) = produceFileName
        strList(1) = geoFileName

        Return strList

    End Function


    Private Function Georeference(ByVal Point1_Img_X As Double, ByVal Point2_Img_X As Double, ByVal Point3_Img_X As Double, ByVal Point1_Img_Y As Double, ByVal Point2_Img_Y As Double, ByVal Point3_Img_Y As Double, ByVal Point1_Lat As Double, ByVal Point2_Lat As Double, ByVal Point3_Lat As Double, ByVal Point1_Long As Double, ByVal Point2_Long As Double, ByVal Point3_Long As Double) As Double()
        'This function creates the text for the world file based on the three points
        'Derived from the Affine Transform and using a little algebra
        'The result should be 6 coefficients
        'A
        'B
        'C
        'D
        'E
        'F
        'Longitude = A * X + C * Y + E
        'Latitude = B * X + D * Y + F
        'So with 3 points, we have 6 equations and 6 unknowns
        'Separate the latitude and longitude equations, and you have two systems with 3 equations and 3 unknowns

        Dim dA, dB, dC, dD, dE, dF As Double
        Dim ln1, ln2, ln3, X1, X2, X3 As Double
        Dim lat1, lat2, lat3, Y1, Y2, Y3 As Double
        Dim gamma As Double

        'You could obtain these values using any method, but my values are in textboxes
        ln1 = Val(Point1_Long)
        ln2 = Val(Point2_Long)
        ln3 = Val(Point3_Long)
        X1 = Val(Point1_Img_X)
        X2 = Val(Point2_Img_X)
        X3 = Val(Point3_Img_X)
        lat1 = Val(Point1_Lat)
        lat2 = Val(Point2_Lat)
        lat3 = Val(Point3_Lat)
        Y1 = Val(Point1_Img_Y)
        Y2 = Val(Point2_Img_Y)
        Y3 = Val(Point3_Img_Y)

        'Prevent possible division by 0

        If (Y2 - Y3) = 0 Then
            gamma = 0
        Else
            gamma = (Y1 - Y3) / (Y2 - Y3)
        End If

        'Longitude based Coefficients

        dA = (ln1 - ln3 - gamma * (ln2 - ln3)) / (X1 - X3 + gamma * (X3 - X2))

        If Y2 - Y3 = 0 Then 'Prevent division by 0
            dC = ln2 - ln3
        Else
            dC = (ln2 - ln3 + dA * (X3 - X2)) / (Y2 - Y3)
        End If

        dE = ln3 - dA * X3 - dC * Y3

        'Latitude based Coefficients

        dB = (lat1 - lat3 - gamma * (lat2 - lat3)) / (X1 - X3 + gamma * (X3 - X2))

        If Y2 - Y3 = 0 Then 'Prevent division by 0
            dD = 0
        Else
            dD = (lat2 - lat3 + dB * (X3 - X2)) / (Y2 - Y3)
        End If

        dF = lat3 - dB * X3 - dD * Y3

        Dim retVal(5) As Double

        retVal(0) = dA
        retVal(1) = dB
        retVal(2) = dC
        retVal(3) = -dD
        retVal(4) = dE
        retVal(5) = dF + (dD * CDbl(imgheight))

        Return retVal
    End Function

    Private Function RectifyNonzeroRotation(ByRef imgFilename As String, ByVal Coeffs As Double()) As Double()
        Try
            Dim img As New System.Drawing.Bitmap(imgFilename)

            If img Is Nothing Then

                Return Nothing
            End If

            If Not Coeffs.Length = 6 Then

                Return Nothing
            End If

            Return RectifyNonzeroRotation(img, imgFilename, Coeffs(0), Coeffs(1), Coeffs(2), Coeffs(3), Coeffs(4), Coeffs(5))
        Catch ex As Exception

            System.Windows.Forms.MessageBox.Show(ex.Message)
            Return Nothing
        End Try
    End Function

    Private Function RectifyNonzeroRotation(ByRef Source As System.Drawing.Bitmap, ByVal outputFilename As String, ByVal dA As Double, ByVal dB As Double, ByVal dC As Double, ByVal dD As Double, ByVal dE As Double, ByVal dF As Double) As Double()
        'dA, dB, dC, dD, dE, dF  -- Hold the previously calculated Affine 
        'Source -- an already loaded but unrectified bitmap
        Dim dX, dY, extraX, extraY As Integer
        Dim Output As Bitmap
        Dim G As Graphics
        Dim M As Drawing2D.Matrix

        '查看此检查的必要性，以确定是否保留

        'If dA = 0 Or dD = 0 Or dE = 0 Or dF = 0 Then
        '    MessageBox.Show("The georeferencing step does not appear to have completed successfully. Please double-check the coordinates (perhaps a flippex X/Y for Long/Lat?)", "Georeferencing FAiled", MessageBoxButtons.OK, MessageBoxIcon.Exclamation)
        '    Return Nothing
        'End If

        'We will create a large enough bitmap to hold the entire rotated image
        extraX = Math.Abs(dC / dA * Source.Height)
        extraY = Math.Abs(dB / dD * Source.Width)
        Output = New Bitmap(Source.Width + extraX, Source.Height + extraY)
        G = Graphics.FromImage(Output)

        'If the coefficients are in alphabetical order in the world file:
        '
        'A
        'B
        'C
        'D
        'E
        'F
        '
        'X1 = Ax + Cy + E
        'Y1 = Bx + Dy + F
        '
        'As an example, if you wanted to literally draw the projected image
        'using the affine coordinates, you would use the following Matrix:
        '
        '| A     B     0 |   
        '|               |       
        '| C     D     0 |    
        '|               |      
        '| E     F     1 |    
        '
        'But to correct the image, we just need to rotate and skew,
        'specified by B and C.  However, since our B and C terms are in
        'world coordinates, we need to use A and D in order to calculate 
        'the skew/rotation in pixel coordinates
        '
        '|   1        B/D          0      |
        '|                                |
        '|   C/A       1           0      |
        '|                                |
        '|   dX       dY           1      |
        '
        '
        'Sometimes the corrected image will have some blank space at the top
        'left corner, so we need to account for that in our output image by
        'shifting the target location for the top left corner if necessary.

        'If the bottom is further left than the top left corner, shift the image right
        If dC < 0 And dA > 0 Then dX = extraX
        If dC > 0 And dA < 0 Then dX = -extraX

        'If the right edge is higher up than the top left corner, shift the image down
        If dB > 0 And dD < 0 Then dY = -extraY
        If dB < 0 And dD > 0 Then dY = extraY

        'Create the transform matrix
        M = New Drawing2D.Matrix(1, dB / dD, dC / dA, 1, dX, dY)
        G.Transform = M

        'Draw the transformed of our existing image "Source"
        G.DrawImage(Source, 0, 0, Source.Width + extraX, Source.Height + extraY)

        'Save the rectified image to the original filename with "prj" added to it
        Dim filename As String = System.IO.Path.GetFileNameWithoutExtension(outputFilename) + "_Rectified" + System.IO.Path.GetExtension(outputFilename)

        '这里是我自己的代码
        produceFileName = filename



        '.NET isn't smart enough to detect filetype from extension:
        If filename.ToLower().EndsWith(".bmp") Then
            Output.Save(filename, System.Drawing.Imaging.ImageFormat.Bmp)
        ElseIf filename.ToLower().EndsWith(".jpg") Then
            Output.Save(filename, System.Drawing.Imaging.ImageFormat.Jpeg)
        ElseIf filename.ToLower().EndsWith(".gif") Then
            Output.Save(filename, System.Drawing.Imaging.ImageFormat.Gif)
        ElseIf filename.ToLower().EndsWith(".png") Then
            Output.Save(filename, System.Drawing.Imaging.ImageFormat.Png)
        ElseIf filename.ToLower().EndsWith(".tif") Or filename.ToLower().EndsWith(".tiff") Then
            Output.Save(filename, System.Drawing.Imaging.ImageFormat.Tiff)
        ElseIf filename.ToLower().EndsWith(".emf") Then
            Output.Save(filename, System.Drawing.Imaging.ImageFormat.Emf)
        ElseIf filename.ToLower().EndsWith(".exif") Then
            Output.Save(filename, System.Drawing.Imaging.ImageFormat.Exif)
        ElseIf filename.ToLower().EndsWith(".wmf") Then
            Output.Save(filename, System.Drawing.Imaging.ImageFormat.Wmf)
        Else
            'Take our chances
            Output.Save(filename)
        End If

        'New rectified coordinates
        dB = 0
        dC = 0
        dE = dE - dX * dA
        dF = dF - dY * dD

        'Textbox showing coordinates
        Dim retVal(5) As Double

        retVal(0) = dA
        retVal(1) = dB
        retVal(2) = dC
        retVal(3) = dD
        retVal(4) = dE
        retVal(5) = dF

        Return retVal
    End Function

    Private Sub CreateWorldFile(ByVal imageFilename As String, ByVal Coeffs As Double())
        'Create the world file for the rectified image
        'Do this using as the extention the first letter of the original image filename,
        'then the last letter of the image, then the letter "w".
        'i.e., for .JPG, the world file will be .JGW
        'for .BMP, it will be .BPW




        Dim outputFilename As String = imageFilename.Substring(0, imageFilename.Length - 2)
        outputFilename += imageFilename.Substring(imageFilename.Length - 1)
        outputFilename += "w"


        If System.IO.File.Exists(outputFilename) Then

            System.IO.File.SetAttributes(outputFilename, IO.FileAttributes.Normal)
            System.IO.File.Delete(outputFilename)
        End If


        '为了输出参数，我拼了
        geoFileName = outputFilename

        Dim sw As System.IO.StreamWriter = System.IO.File.CreateText(outputFilename)
        sw.WriteLine(Coeffs(0).ToString)
        sw.WriteLine(Coeffs(1).ToString)
        sw.WriteLine(Coeffs(2).ToString)
        sw.WriteLine(Coeffs(3).ToString)
        sw.WriteLine(Coeffs(4).ToString)
        sw.WriteLine(Coeffs(5).ToString)
        sw.Close()



    End Sub


    Private Sub DoCompute(ByVal pt1x As Double, ByVal pt2x As Double, ByVal pt3x As Double, ByVal pt1y As Double, ByVal pt2y As Double, ByVal pt3y As Double, ByVal pt1l As Double, ByVal pt2l As Double, ByVal pt3l As Double, ByVal pt1g As Double, ByVal pt2g As Double, ByVal pt3g As Double, Optional ByVal bForceNorth As Boolean = False)




        Dim Coeffs() As Double = Georeference(pt1x, pt2x, pt3x, pt1y, pt2y, pt3y, pt1l, pt2l, pt3l, pt1g, pt2g, pt3g)
        Dim RectifiedCoeffs() As Double = Nothing

        If (Not Coeffs(1) = 0 Or Not Coeffs(2) = 0) And bForceNorth Then

            RectifiedCoeffs = RectifyNonzeroRotation(workingFilename, Coeffs)
        End If

        If Not RectifiedCoeffs Is Nothing Then
            CreateWorldFile(System.IO.Path.GetFileNameWithoutExtension(workingFilename) + "_Rectified" + System.IO.Path.GetExtension(workingFilename), RectifiedCoeffs)

        ElseIf Not Coeffs Is Nothing Then
            CreateWorldFile(workingFilename, Coeffs)

        End If

        '输出文件名一般为 w文件名+_Rectified+后缀

    End Sub



End Class
